/*
===========================================================================

Wolfenstein: Enemy Territory GPL Source Code
Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. 

This file is part of the Wolfenstein: Enemy Territory GPL Source Code (Wolf ET Source Code).  

Wolf ET Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Wolf ET Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Wolf ET Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Wolf: ET Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Wolf ET Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/


/*****************************************************************************
 * name:		l_script.c
 *
 * desc:		lexicographical parser
 *
 *
 *****************************************************************************/

//#define SCREWUP
//#define BOTLIB
//#define MEQCC
//#define BSPC

#ifdef SCREWUP
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <stdarg.h>
#include "l_memory.h"
#include "l_script.h"

typedef enum {qfalse, qtrue}    qboolean;

#endif //SCREWUP

#ifdef BOTLIB
//include files for usage in the bot library
#include "../game/q_shared.h"
#include "botlib.h"
#include "be_interface.h"
#include "l_script.h"
#include "l_memory.h"
#include "l_log.h"
#include "l_libvar.h"
#endif //BOTLIB

#ifdef MEQCC
//include files for usage in MrElusive's QuakeC Compiler
#include "qcc.h"
#include "l_script.h"
#include "l_memory.h"
#include "l_log.h"

#define qtrue   true
#define qfalse  false
#endif //MEQCC

#ifdef BSPC
//include files for usage in the BSP Converter
#include "../bspc/qbsp.h"
#include "../bspc/l_log.h"
#include "../bspc/l_mem.h"

#define qtrue   true
#define qfalse  false
#endif //BSPC

int Q_vsnprintf( char *dest, int size, const char *fmt, va_list argptr );

#define PUNCTABLE

//longer punctuations first
punctuation_t default_punctuations[] =
{
	//binary operators
	{">>=",P_RSHIFT_ASSIGN, NULL},
	{"<<=",P_LSHIFT_ASSIGN, NULL},
	//
	{"...",P_PARMS, NULL},
	//define merge operator
	{"##",P_PRECOMPMERGE, NULL},
	//logic operators
	{"&&",P_LOGIC_AND, NULL},
	{"||",P_LOGIC_OR, NULL},
	{">=",P_LOGIC_GEQ, NULL},
	{"<=",P_LOGIC_LEQ, NULL},
	{"==",P_LOGIC_EQ, NULL},
	{"!=",P_LOGIC_UNEQ, NULL},
	//arithmatic operators
	{"*=",P_MUL_ASSIGN, NULL},
	{"/=",P_DIV_ASSIGN, NULL},
	{"%=",P_MOD_ASSIGN, NULL},
	{"+=",P_ADD_ASSIGN, NULL},
	{"-=",P_SUB_ASSIGN, NULL},
	{"++",P_INC, NULL},
	{"--",P_DEC, NULL},
	//binary operators
	{"&=",P_BIN_AND_ASSIGN, NULL},
	{"|=",P_BIN_OR_ASSIGN, NULL},
	{"^=",P_BIN_XOR_ASSIGN, NULL},
	{">>",P_RSHIFT, NULL},
	{"<<",P_LSHIFT, NULL},
	//reference operators
	{"->",P_POINTERREF, NULL},
	//C++
	{"::",P_CPP1, NULL},
	{".*",P_CPP2, NULL},
	//arithmatic operators
	{"*",P_MUL, NULL},
	{"/",P_DIV, NULL},
	{"%",P_MOD, NULL},
	{"+",P_ADD, NULL},
	{"-",P_SUB, NULL},
	{"=",P_ASSIGN, NULL},
	//binary operators
	{"&",P_BIN_AND, NULL},
	{"|",P_BIN_OR, NULL},
	{"^",P_BIN_XOR, NULL},
	{"~",P_BIN_NOT, NULL},
	//logic operators
	{"!",P_LOGIC_NOT, NULL},
	{">",P_LOGIC_GREATER, NULL},
	{"<",P_LOGIC_LESS, NULL},
	//reference operator
	{".",P_REF, NULL},
	//seperators
	{",",P_COMMA, NULL},
	{";",P_SEMICOLON, NULL},
	//label indication
	{":",P_COLON, NULL},
	//if statement
	{"?",P_QUESTIONMARK, NULL},
	//embracements
	{"(",P_PARENTHESESOPEN, NULL},
	{")",P_PARENTHESESCLOSE, NULL},
	{"{",P_BRACEOPEN, NULL},
	{"}",P_BRACECLOSE, NULL},
	{"[",P_SQBRACKETOPEN, NULL},
	{"]",P_SQBRACKETCLOSE, NULL},
	//
	{"\\",P_BACKSLASH, NULL},
	//precompiler operator
	{"#",P_PRECOMP, NULL},
#ifdef DOLLAR
	{"$",P_DOLLAR, NULL},
#endif //DOLLAR
	{NULL, 0}
};

//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void PS_CreatePunctuationTable( script_t *script, punctuation_t *punctuations ) {
	int i;
	punctuation_t *p, *lastp, *newp;

	//get memory for the table
	if ( !script->punctuationtable ) {
		script->punctuationtable = (punctuation_t **)
								   GetMemory( 256 * sizeof( punctuation_t * ) );
	}
	memset( script->punctuationtable, 0, 256 * sizeof( punctuation_t * ) );
	//add the punctuations in the list to the punctuation table
	for ( i = 0; punctuations[i].p; i++ )
	{
		newp = &punctuations[i];
		lastp = NULL;
		//sort the punctuations in this table entry on length (longer punctuations first)
		for ( p = script->punctuationtable[(unsigned int) newp->p[0]]; p; p = p->next )
		{
			if ( strlen( p->p ) < strlen( newp->p ) ) {
				newp->next = p;
				if ( lastp ) {
					lastp->next = newp;
				} else { script->punctuationtable[(unsigned int) newp->p[0]] = newp;}
				break;
			} //end if
			lastp = p;
		} //end for
		if ( !p ) {
			newp->next = NULL;
			if ( lastp ) {
				lastp->next = newp;
			} else { script->punctuationtable[(unsigned int) newp->p[0]] = newp;}
		} //end if
	} //end for
} //end of the function PS_CreatePunctuationTable
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
char *PunctuationFromNum( script_t *script, int num ) {
	int i;

	for ( i = 0; script->punctuations[i].p; i++ )
	{
		if ( script->punctuations[i].n == num ) {
			return script->punctuations[i].p;
		}
	} //end for
	return "unkown punctuation";
} //end of the function PunctuationFromNum
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void QDECL ScriptError( script_t *script, char *str, ... ) {
	char text[1024];
	va_list ap;

	if ( script->flags & SCFL_NOERRORS ) {
		return;
	}

	va_start( ap, str );
	Q_vsnprintf( text, sizeof( text ), str, ap );
	va_end( ap );
#ifdef BOTLIB
	botimport.Print( PRT_ERROR, "file %s, line %d: %s\n", script->filename, script->line, text );
#endif //BOTLIB
#ifdef MEQCC
	printf( "error: file %s, line %d: %s\n", script->filename, script->line, text );
#endif //MEQCC
#ifdef BSPC
	Log_Print( "error: file %s, line %d: %s\n", script->filename, script->line, text );
#endif //BSPC
} //end of the function ScriptError
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void QDECL ScriptWarning( script_t *script, char *str, ... ) {
	char text[1024];
	va_list ap;

	if ( script->flags & SCFL_NOWARNINGS ) {
		return;
	}

	va_start( ap, str );
	Q_vsnprintf( text, sizeof( text ), str, ap );
	va_end( ap );
#ifdef BOTLIB
	botimport.Print( PRT_WARNING, "file %s, line %d: %s\n", script->filename, script->line, text );
#endif //BOTLIB
#ifdef MEQCC
	printf( "warning: file %s, line %d: %s\n", script->filename, script->line, text );
#endif //MEQCC
#ifdef BSPC
	Log_Print( "warning: file %s, line %d: %s\n", script->filename, script->line, text );
#endif //BSPC
} //end of the function ScriptWarning
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void SetScriptPunctuations( script_t *script, punctuation_t *p ) {
#ifdef PUNCTABLE
	if ( p ) {
		PS_CreatePunctuationTable( script, p );
	} else { PS_CreatePunctuationTable( script, default_punctuations );}
#endif //PUNCTABLE
	if ( p ) {
		script->punctuations = p;
	} else { script->punctuations = default_punctuations;}
} //end of the function SetScriptPunctuations
//============================================================================
// Reads spaces, tabs, C-like comments etc.
// When a newline character is found the scripts line counter is increased.
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadWhiteSpace( script_t *script ) {
	while ( 1 )
	{
		//skip white space
		while ( *script->script_p <= ' ' )
		{
			if ( !*script->script_p ) {
				return 0;
			}
			if ( *script->script_p == '\n' ) {
				script->line++;
			}
			script->script_p++;
		} //end while
		  //skip comments
		if ( *script->script_p == '/' ) {
			//comments //
			if ( *( script->script_p + 1 ) == '/' ) {
				script->script_p++;
				do
				{
					script->script_p++;
					if ( !*script->script_p ) {
						return 0;
					}
				} //end do
				while ( *script->script_p != '\n' );
				script->line++;
				script->script_p++;
				if ( !*script->script_p ) {
					return 0;
				}
				continue;
			} //end if
			  //comments /* */
			else if ( *( script->script_p + 1 ) == '*' ) {
				script->script_p++;
				do
				{
					script->script_p++;
					if ( !*script->script_p ) {
						return 0;
					}
					if ( *script->script_p == '\n' ) {
						script->line++;
					}
				} //end do
				while ( !( *script->script_p == '*' && *( script->script_p + 1 ) == '/' ) );
				script->script_p++;
				if ( !*script->script_p ) {
					return 0;
				}
				script->script_p++;
				if ( !*script->script_p ) {
					return 0;
				}
				continue;
			} //end if
		} //end if
		break;
	} //end while
	return 1;
} //end of the function PS_ReadWhiteSpace
//============================================================================
// Reads an escape character.
//
// Parameter:				script		: script to read from
//								ch				: place to store the read escape character
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadEscapeCharacter( script_t *script, char *ch ) {
	int c, val, i;

	//step over the leading '\\'
	script->script_p++;
	//determine the escape character
	switch ( *script->script_p )
	{
	case '\\': c = '\\'; break;
	case 'n': c = '\n'; break;
	case 'r': c = '\r'; break;
	case 't': c = '\t'; break;
	case 'v': c = '\v'; break;
	case 'b': c = '\b'; break;
	case 'f': c = '\f'; break;
	case 'a': c = '\a'; break;
	case '\'': c = '\''; break;
	case '\"': c = '\"'; break;
	case '\?': c = '\?'; break;
	case 'x':
	{
		script->script_p++;
		for ( i = 0, val = 0; ; i++, script->script_p++ )
		{
			c = *script->script_p;
			if ( c >= '0' && c <= '9' ) {
				c = c - '0';
			} else if ( c >= 'A' && c <= 'Z' ) {
				c = c - 'A' + 10;
			} else if ( c >= 'a' && c <= 'z' )                                            {
				c = c - 'a' + 10;
			} else { break;}
			val = ( val << 4 ) + c;
		}     //end for
		script->script_p--;
		if ( val > 0xFF ) {
			ScriptWarning( script, "too large value in escape character" );
			val = 0xFF;
		}     //end if
		c = val;
		break;
	}     //end case
	default:     //NOTE: decimal ASCII code, NOT octal
	{
		if ( *script->script_p < '0' || *script->script_p > '9' ) {
			ScriptError( script, "unknown escape char" );
		}
		for ( i = 0, val = 0; ; i++, script->script_p++ )
		{
			c = *script->script_p;
			if ( c >= '0' && c <= '9' ) {
				c = c - '0';
			} else { break;}
			val = val * 10 + c;
		}     //end for
		script->script_p--;
		if ( val > 0xFF ) {
			ScriptWarning( script, "too large value in escape character" );
			val = 0xFF;
		}     //end if
		c = val;
		break;
	}     //end default
	} //end switch
	  //step over the escape character or the last digit of the number
	script->script_p++;
	//store the escape character
	*ch = c;
	//succesfully read escape character
	return 1;
} //end of the function PS_ReadEscapeCharacter
//============================================================================
// Reads C-like string. Escape characters are interpretted.
// Quotes are included with the string.
// Reads two strings with a white space between them as one string.
//
// Parameter:				script		: script to read from
//								token			: buffer to store the string
// Returns:					qtrue when a string was read succesfully
// Changes Globals:		-
//============================================================================
int PS_ReadString( script_t *script, token_t *token, int quote ) {
	int len, tmpline;
	char *tmpscript_p;

	if ( quote == '\"' ) {
		token->type = TT_STRING;
	} else { token->type = TT_LITERAL;}

	len = 0;
	//leading quote
	token->string[len++] = *script->script_p++;
	//
	while ( 1 )
	{
		//minus 2 because trailing double quote and zero have to be appended
		if ( len >= MAX_TOKEN - 2 ) {
			ScriptError( script, "string longer than MAX_TOKEN = %d", MAX_TOKEN );
			return 0;
		} //end if
		  //if there is an escape character and
		  //if escape characters inside a string are allowed
		if ( *script->script_p == '\\' && !( script->flags & SCFL_NOSTRINGESCAPECHARS ) ) {
			if ( !PS_ReadEscapeCharacter( script, &token->string[len] ) ) {
				token->string[len] = 0;
				return 0;
			} //end if
			len++;
		} //end if
		  //if a trailing quote
		else if ( *script->script_p == quote ) {
			//step over the double quote
			script->script_p++;
			//if white spaces in a string are not allowed
			if ( script->flags & SCFL_NOSTRINGWHITESPACES ) {
				break;
			}
			//
			tmpscript_p = script->script_p;
			tmpline = script->line;
			//read unusefull stuff between possible two following strings
			if ( !PS_ReadWhiteSpace( script ) ) {
				script->script_p = tmpscript_p;
				script->line = tmpline;
				break;
			} //end if
			  //if there's no leading double qoute
			if ( *script->script_p != quote ) {
				script->script_p = tmpscript_p;
				script->line = tmpline;
				break;
			} //end if
			  //step over the new leading double quote
			script->script_p++;
		} //end if
		else
		{
			if ( *script->script_p == '\0' ) {
				token->string[len] = 0;
				ScriptError( script, "missing trailing quote" );
				return 0;
			} //end if
			if ( *script->script_p == '\n' ) {
				token->string[len] = 0;
				ScriptError( script, "newline inside string %s", token->string );
				return 0;
			} //end if
			token->string[len++] = *script->script_p++;
		} //end else
	} //end while
	  //trailing quote
	token->string[len++] = quote;
	//end string with a zero
	token->string[len] = '\0';
	//the sub type is the length of the string
	token->subtype = len;
	return 1;
} //end of the function PS_ReadString
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadName( script_t *script, token_t *token ) {
	int len = 0;
	char c;

	token->type = TT_NAME;
	do
	{
		token->string[len++] = *script->script_p++;
		if ( len >= MAX_TOKEN ) {
			ScriptError( script, "name longer than MAX_TOKEN = %d", MAX_TOKEN );
			return 0;
		} //end if
		c = *script->script_p;
	} while ( ( c >= 'a' && c <= 'z' ) ||
			  ( c >= 'A' && c <= 'Z' ) ||
			  ( c >= '0' && c <= '9' ) ||
			  c == '_' );
	token->string[len] = '\0';
	//the sub type is the length of the name
	token->subtype = len;
	return 1;
} //end of the function PS_ReadName
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void NumberValue( char *string, int subtype, unsigned long int *intvalue,
				  long double *floatvalue ) {
	unsigned long int dotfound = 0;

	*intvalue = 0;
	*floatvalue = 0;
	//floating point number
	if ( subtype & TT_FLOAT ) {
		while ( *string )
		{
			if ( *string == '.' ) {
				if ( dotfound ) {
					return;
				}
				dotfound = 10;
				string++;
			} //end if
			if ( dotfound ) {
				*floatvalue = *floatvalue + ( long double )( *string - '0' ) /
							  (long double) dotfound;
				dotfound *= 10;
			} //end if
			else
			{
				*floatvalue = *floatvalue * 10.0 + ( long double )( *string - '0' );
			} //end else
			string++;
		} //end while
		*intvalue = (unsigned long) *floatvalue;
	} //end if
	else if ( subtype & TT_DECIMAL ) {
		while ( *string ) *intvalue = *intvalue * 10 + ( *string++ - '0' );
		*floatvalue = *intvalue;
	} //end else if
	else if ( subtype & TT_HEX ) {
		//step over the leading 0x or 0X
		string += 2;
		while ( *string )
		{
			*intvalue <<= 4;
			if ( *string >= 'a' && *string <= 'f' ) {
				*intvalue += *string - 'a' + 10;
			} else if ( *string >= 'A' && *string <= 'F' ) {
				*intvalue += *string - 'A' + 10;
			} else { *intvalue += *string - '0';}
			string++;
		} //end while
		*floatvalue = *intvalue;
	} //end else if
	else if ( subtype & TT_OCTAL ) {
		//step over the first zero
		string += 1;
		while ( *string ) *intvalue = ( *intvalue << 3 ) + ( *string++ - '0' );
		*floatvalue = *intvalue;
	} //end else if
	else if ( subtype & TT_BINARY ) {
		//step over the leading 0b or 0B
		string += 2;
		while ( *string ) *intvalue = ( *intvalue << 1 ) + ( *string++ - '0' );
		*floatvalue = *intvalue;
	} //end else if
} //end of the function NumberValue
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadNumber( script_t *script, token_t *token ) {
	int len = 0, i;
	int octal, dot;
	char c;
//	unsigned long int intvalue = 0;
//	long double floatvalue = 0;

	token->type = TT_NUMBER;
	//check for a hexadecimal number
	if ( *script->script_p == '0' &&
		 ( *( script->script_p + 1 ) == 'x' ||
		   *( script->script_p + 1 ) == 'X' ) ) {
		token->string[len++] = *script->script_p++;
		token->string[len++] = *script->script_p++;
		c = *script->script_p;
		//hexadecimal
		while ( ( c >= '0' && c <= '9' ) ||
				( c >= 'a' && c <= 'f' ) ||
				( c >= 'A' && c <= 'A' ) )
		{
			token->string[len++] = *script->script_p++;
			if ( len >= MAX_TOKEN ) {
				ScriptError( script, "hexadecimal number longer than MAX_TOKEN = %d", MAX_TOKEN );
				return 0;
			} //end if
			c = *script->script_p;
		} //end while
		token->subtype |= TT_HEX;
	} //end if
#ifdef BINARYNUMBERS
	//check for a binary number
	else if ( *script->script_p == '0' &&
			  ( *( script->script_p + 1 ) == 'b' ||
				*( script->script_p + 1 ) == 'B' ) ) {
		token->string[len++] = *script->script_p++;
		token->string[len++] = *script->script_p++;
		c = *script->script_p;
		//hexadecimal
		while ( c == '0' || c == '1' )
		{
			token->string[len++] = *script->script_p++;
			if ( len >= MAX_TOKEN ) {
				ScriptError( script, "binary number longer than MAX_TOKEN = %d", MAX_TOKEN );
				return 0;
			} //end if
			c = *script->script_p;
		} //end while
		token->subtype |= TT_BINARY;
	} //end if
#endif //BINARYNUMBERS
	else //decimal or octal integer or floating point number
	{
		octal = qfalse;
		dot = qfalse;
		if ( *script->script_p == '0' ) {
			octal = qtrue;
		}
		while ( 1 )
		{
			token->string[len++] = *script->script_p++;
			if ( len >= MAX_TOKEN ) {
				ScriptError( script, "number longer than MAX_TOKEN = %d", MAX_TOKEN );
				return 0;
			} //end if
			c = *script->script_p;
			if ( c == '.' ) {
				dot = qtrue;
			} else if ( c == '8' || c == '9' ) {
				octal = qfalse;
			} else if ( c < '0' || c > '9' )                                              {
				break;
			}
		} //end while
		if ( octal ) {
			token->subtype |= TT_OCTAL;
		} else { token->subtype |= TT_DECIMAL;}
		if ( dot ) {
			token->subtype |= TT_FLOAT;
		}
	} //end else
	for ( i = 0; i < 2; i++ )
	{
		c = *script->script_p;
		//check for a LONG number
		if ( c == 'l' || c == 'L' &&
			 !( token->subtype & TT_LONG ) ) {
			script->script_p++;
			token->subtype |= TT_LONG;
		} //end if
		  //check for an UNSIGNED number
		else if ( c == 'u' || c == 'U' &&
				  !( token->subtype & ( TT_UNSIGNED | TT_FLOAT ) ) ) {
			script->script_p++;
			token->subtype |= TT_UNSIGNED;
		} //end if
	} //end for
	token->string[len] = '\0';
#ifdef NUMBERVALUE
	NumberValue( token->string, token->subtype, &token->intvalue, &token->floatvalue );
#endif //NUMBERVALUE
	if ( !( token->subtype & TT_FLOAT ) ) {
		token->subtype |= TT_INTEGER;
	}
	return 1;
} //end of the function PS_ReadNumber
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadLiteral( script_t *script, token_t *token ) {
	token->type = TT_LITERAL;
	//first quote
	token->string[0] = *script->script_p++;
	//check for end of file
	if ( !*script->script_p ) {
		ScriptError( script, "end of file before trailing \'" );
		return 0;
	} //end if
	  //if it is an escape character
	if ( *script->script_p == '\\' ) {
		if ( !PS_ReadEscapeCharacter( script, &token->string[1] ) ) {
			return 0;
		}
	} //end if
	else
	{
		token->string[1] = *script->script_p++;
	} //end else
	  //check for trailing quote
	if ( *script->script_p != '\'' ) {
		ScriptWarning( script, "too many characters in literal, ignored" );
		while ( *script->script_p &&
				*script->script_p != '\'' &&
				*script->script_p != '\n' )
		{
			script->script_p++;
		} //end while
		if ( *script->script_p == '\'' ) {
			script->script_p++;
		}
	} //end if
	  //store the trailing quote
	token->string[2] = *script->script_p++;
	//store trailing zero to end the string
	token->string[3] = '\0';
	//the sub type is the integer literal value
	token->subtype = token->string[1];
	//
	return 1;
} //end of the function PS_ReadLiteral
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadPunctuation( script_t *script, token_t *token ) {
	int len;
	char *p;
	punctuation_t *punc;

#ifdef PUNCTABLE
	for ( punc = script->punctuationtable[(unsigned int)*script->script_p]; punc; punc = punc->next )
	{
#else
	int i;

	for ( i = 0; script->punctuations[i].p; i++ )
	{
		punc = &script->punctuations[i];
#endif //PUNCTABLE
		p = punc->p;
		len = strlen( p );
		//if the script contains at least as much characters as the punctuation
		if ( script->script_p + len <= script->end_p ) {
			//if the script contains the punctuation
			if ( !strncmp( script->script_p, p, len ) ) {
				strncpy( token->string, p, MAX_TOKEN );
				script->script_p += len;
				token->type = TT_PUNCTUATION;
				//sub type is the number of the punctuation
				token->subtype = punc->n;
				return 1;
			} //end if
		} //end if
	} //end for
	return 0;
} //end of the function PS_ReadPunctuation
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadPrimitive( script_t *script, token_t *token ) {
	int len;

	len = 0;
	while ( *script->script_p > ' ' && *script->script_p != ';' )
	{
		if ( len >= MAX_TOKEN ) {
			ScriptError( script, "primitive token longer than MAX_TOKEN = %d", MAX_TOKEN );
			return 0;
		} //end if
		token->string[len++] = *script->script_p++;
	} //end while
	token->string[len] = 0;
	//copy the token into the script structure
	memcpy( &script->token, token, sizeof( token_t ) );
	//primitive reading successfull
	return 1;
} //end of the function PS_ReadPrimitive
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadToken( script_t *script, token_t *token ) {
	//if there is a token available (from UnreadToken)
	if ( script->tokenavailable ) {
		script->tokenavailable = 0;
		memcpy( token, &script->token, sizeof( token_t ) );
		return 1;
	} //end if
	  //save script pointer
	script->lastscript_p = script->script_p;
	//save line counter
	script->lastline = script->line;
	//clear the token stuff
	memset( token, 0, sizeof( token_t ) );
	//start of the white space
	script->whitespace_p = script->script_p;
	token->whitespace_p = script->script_p;
	//read unusefull stuff
	if ( !PS_ReadWhiteSpace( script ) ) {
		return 0;
	}
	//end of the white space
	script->endwhitespace_p = script->script_p;
	token->endwhitespace_p = script->script_p;
	//line the token is on
	token->line = script->line;
	//number of lines crossed before token
	token->linescrossed = script->line - script->lastline;
	//if there is a leading double quote
	if ( *script->script_p == '\"' ) {
		if ( !PS_ReadString( script, token, '\"' ) ) {
			return 0;
		}
	} //end if
	  //if an literal
	else if ( *script->script_p == '\'' ) {
		//if (!PS_ReadLiteral(script, token)) return 0;
		if ( !PS_ReadString( script, token, '\'' ) ) {
			return 0;
		}
	} //end if
	  //if there is a number
	else if ( ( *script->script_p >= '0' && *script->script_p <= '9' ) ||
			  ( *script->script_p == '.' &&
				( *( script->script_p + 1 ) >= '0' && *( script->script_p + 1 ) <= '9' ) ) ) {
		if ( !PS_ReadNumber( script, token ) ) {
			return 0;
		}
	} //end if
	  //if this is a primitive script
	else if ( script->flags & SCFL_PRIMITIVE ) {
		return PS_ReadPrimitive( script, token );
	} //end else if
	  //if there is a name
	else if ( ( *script->script_p >= 'a' && *script->script_p <= 'z' ) ||
			  ( *script->script_p >= 'A' && *script->script_p <= 'Z' ) ||
			  *script->script_p == '_' ) {
		if ( !PS_ReadName( script, token ) ) {
			return 0;
		}
	} //end if
	  //check for punctuations
	else if ( !PS_ReadPunctuation( script, token ) ) {
		ScriptError( script, "can't read token" );
		return 0;
	} //end if
	  //copy the token into the script structure
	memcpy( &script->token, token, sizeof( token_t ) );
	//succesfully read a token
	return 1;
} //end of the function PS_ReadToken
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ExpectTokenString( script_t *script, char *string ) {
	token_t token;

	if ( !PS_ReadToken( script, &token ) ) {
		ScriptError( script, "couldn't find expected %s", string );
		return 0;
	} //end if

	if ( strcmp( token.string, string ) ) {
		ScriptError( script, "expected %s, found %s", string, token.string );
		return 0;
	} //end if
	return 1;
} //end of the function PS_ExpectToken
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ExpectTokenType( script_t *script, int type, int subtype, token_t *token ) {
	char str[MAX_TOKEN];

	if ( !PS_ReadToken( script, token ) ) {
		ScriptError( script, "couldn't read expected token" );
		return 0;
	} //end if

	if ( token->type != type ) {
		if ( type == TT_STRING ) {
			strcpy( str, "string" );
		}
		if ( type == TT_LITERAL ) {
			strcpy( str, "literal" );
		}
		if ( type == TT_NUMBER ) {
			strcpy( str, "number" );
		}
		if ( type == TT_NAME ) {
			strcpy( str, "name" );
		}
		if ( type == TT_PUNCTUATION ) {
			strcpy( str, "punctuation" );
		}
		ScriptError( script, "expected a %s, found %s", str, token->string );
		return 0;
	} //end if
	if ( token->type == TT_NUMBER ) {
		if ( ( token->subtype & subtype ) != subtype ) {
			if ( subtype & TT_DECIMAL ) {
				strcpy( str, "decimal" );
			}
			if ( subtype & TT_HEX ) {
				strcpy( str, "hex" );
			}
			if ( subtype & TT_OCTAL ) {
				strcpy( str, "octal" );
			}
			if ( subtype & TT_BINARY ) {
				strcpy( str, "binary" );
			}
			if ( subtype & TT_LONG ) {
				strcat( str, " long" );
			}
			if ( subtype & TT_UNSIGNED ) {
				strcat( str, " unsigned" );
			}
			if ( subtype & TT_FLOAT ) {
				strcat( str, " float" );
			}
			if ( subtype & TT_INTEGER ) {
				strcat( str, " integer" );
			}
			ScriptError( script, "expected %s, found %s", str, token->string );
			return 0;
		} //end if
	} //end if
	else if ( token->type == TT_PUNCTUATION ) {
		if ( subtype < 0 ) {
			ScriptError( script, "BUG: wrong punctuation subtype" );
			return 0;
		} //end if
		if ( token->subtype != subtype ) {
			ScriptError( script, "expected %s, found %s",
						 script->punctuations[subtype], token->string );
			return 0;
		} //end if
	} //end else if
	return 1;
} //end of the function PS_ExpectTokenType
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ExpectAnyToken( script_t *script, token_t *token ) {
	if ( !PS_ReadToken( script, token ) ) {
		ScriptError( script, "couldn't read expected token" );
		return 0;
	} //end if
	else
	{
		return 1;
	} //end else
} //end of the function PS_ExpectAnyToken
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_CheckTokenString( script_t *script, char *string ) {
	token_t tok;

	if ( !PS_ReadToken( script, &tok ) ) {
		return 0;
	}
	//if the token is available
	if ( !strcmp( tok.string, string ) ) {
		return 1;
	}
	//token not available
	script->script_p = script->lastscript_p;
	return 0;
} //end of the function PS_CheckTokenString
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_CheckTokenType( script_t *script, int type, int subtype, token_t *token ) {
	token_t tok;

	if ( !PS_ReadToken( script, &tok ) ) {
		return 0;
	}
	//if the type matches
	if ( tok.type == type &&
		 ( tok.subtype & subtype ) == subtype ) {
		memcpy( token, &tok, sizeof( token_t ) );
		return 1;
	} //end if
	  //token is not available
	script->script_p = script->lastscript_p;
	return 0;
} //end of the function PS_CheckTokenType
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_SkipUntilString( script_t *script, char *string ) {
	token_t token;

	while ( PS_ReadToken( script, &token ) )
	{
		if ( !strcmp( token.string, string ) ) {
			return 1;
		}
	} //end while
	return 0;
} //end of the function PS_SkipUntilString
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void PS_UnreadLastToken( script_t *script ) {
	script->tokenavailable = 1;
} //end of the function UnreadLastToken
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void PS_UnreadToken( script_t *script, token_t *token ) {
	memcpy( &script->token, token, sizeof( token_t ) );
	script->tokenavailable = 1;
} //end of the function UnreadToken
//============================================================================
// returns the next character of the read white space, returns NULL if none
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
char PS_NextWhiteSpaceChar( script_t *script ) {
	if ( script->whitespace_p != script->endwhitespace_p ) {
		return *script->whitespace_p++;
	} //end if
	else
	{
		return 0;
	} //end else
} //end of the function PS_NextWhiteSpaceChar
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void StripDoubleQuotes( char *string ) {
	if ( *string == '\"' ) {
		//strcpy(string, string+1);
		// rain - strcpy arguments cannot overlap, memmove string+1 and NUL
		memmove( string, string + 1, strlen( string + 1 ) + 1 );
	} //end if
	if ( string[strlen( string ) - 1] == '\"' ) {
		string[strlen( string ) - 1] = '\0';
	} //end if
} //end of the function StripDoubleQuotes
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void StripSingleQuotes( char *string ) {
	if ( *string == '\'' ) {
		strcpy( string, string + 1 );
	} //end if
	if ( string[strlen( string ) - 1] == '\'' ) {
		string[strlen( string ) - 1] = '\0';
	} //end if
} //end of the function StripSingleQuotes
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
long double ReadSignedFloat( script_t *script ) {
	token_t token;
	long double sign = 1;

	PS_ExpectAnyToken( script, &token );
	if ( !strcmp( token.string, "-" ) ) {
		sign = -1;
		PS_ExpectTokenType( script, TT_NUMBER, 0, &token );
	} //end if
	else if ( token.type != TT_NUMBER ) {
		ScriptError( script, "expected float value, found %s\n", token.string );
	} //end else if
	return sign * token.floatvalue;
} //end of the function ReadSignedFloat
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
signed long int ReadSignedInt( script_t *script ) {
	token_t token;
	signed long int sign = 1;

	PS_ExpectAnyToken( script, &token );
	if ( !strcmp( token.string, "-" ) ) {
		sign = -1;
		PS_ExpectTokenType( script, TT_NUMBER, TT_INTEGER, &token );
	} //end if
	else if ( token.type != TT_NUMBER || token.subtype == TT_FLOAT ) {
		ScriptError( script, "expected integer value, found %s\n", token.string );
	} //end else if
	return sign * token.intvalue;
} //end of the function ReadSignedInt
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void SetScriptFlags( script_t *script, int flags ) {
	script->flags = flags;
} //end of the function SetScriptFlags
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int GetScriptFlags( script_t *script ) {
	return script->flags;
} //end of the function GetScriptFlags
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void ResetScript( script_t *script ) {
	//pointer in script buffer
	script->script_p = script->buffer;
	//pointer in script buffer before reading token
	script->lastscript_p = script->buffer;
	//begin of white space
	script->whitespace_p = NULL;
	//end of white space
	script->endwhitespace_p = NULL;
	//set if there's a token available in script->token
	script->tokenavailable = 0;
	//
	script->line = 1;
	script->lastline = 1;
	//clear the saved token
	memset( &script->token, 0, sizeof( token_t ) );
} //end of the function ResetScript
//============================================================================
// returns true if at the end of the script
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int EndOfScript( script_t *script ) {
	return script->script_p >= script->end_p;
} //end of the function EndOfScript
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int NumLinesCrossed( script_t *script ) {
	return script->line - script->lastline;
} //end of the function NumLinesCrossed
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int ScriptSkipTo( script_t *script, char *value ) {
	int len;
	char firstchar;

	firstchar = *value;
	len = strlen( value );
	do
	{
		if ( !PS_ReadWhiteSpace( script ) ) {
			return 0;
		}
		if ( *script->script_p == firstchar ) {
			if ( !strncmp( script->script_p, value, len ) ) {
				return 1;
			} //end if
		} //end if
		script->script_p++;
	} while ( 1 );
} //end of the function ScriptSkipTo
#ifndef BOTLIB
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int FileLength( FILE *fp ) {
	int pos;
	int end;

	pos = ftell( fp );
	fseek( fp, 0, SEEK_END );
	end = ftell( fp );
	fseek( fp, pos, SEEK_SET );

	return end;
} //end of the function FileLength
#endif
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
script_t *LoadScriptFile( char *filename ) {
#ifdef BOTLIB
	fileHandle_t fp;
	char pathname[MAX_QPATH];
#else
	FILE *fp;
#endif
	int length;
	void *buffer;
	script_t *script;

#ifdef BOTLIB
	Com_sprintf( pathname, MAX_QPATH, "botfiles/%s", filename );
	length = botimport.FS_FOpenFile( pathname, &fp, FS_READ );
	if ( !fp ) {
		return NULL;
	}
#else
	fp = fopen( filename, "rb" );
	if ( !fp ) {
		return NULL;
	}

	length = FileLength( fp );
#endif

	buffer = GetClearedMemory( sizeof( script_t ) + length + 1 );
	script = (script_t *) buffer;
	memset( script, 0, sizeof( script_t ) );
	strcpy( script->filename, filename );
	script->buffer = (char *) buffer + sizeof( script_t );
	script->buffer[length] = 0;
	script->length = length;
	//pointer in script buffer
	script->script_p = script->buffer;
	//pointer in script buffer before reading token
	script->lastscript_p = script->buffer;
	//pointer to end of script buffer
	script->end_p = &script->buffer[length];
	//set if there's a token available in script->token
	script->tokenavailable = 0;
	//
	script->line = 1;
	script->lastline = 1;
	//
	SetScriptPunctuations( script, NULL );
	//
#ifdef BOTLIB
	botimport.FS_Read( script->buffer, length, fp );
	botimport.FS_FCloseFile( fp );
#else
	if ( fread( script->buffer, length, 1, fp ) != 1 ) {
		FreeMemory( buffer );
		script = NULL;
	} //end if
	fclose( fp );
#endif
	//
	return script;
} //end of the function LoadScriptFile
//============================================================================
//load a script from the given memory with the given length
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
script_t *LoadScriptMemory( char *ptr, int length, char *name ) {
	void *buffer;
	script_t *script;

	buffer = GetClearedMemory( sizeof( script_t ) + length + 1 );
	script = (script_t *) buffer;
	memset( script, 0, sizeof( script_t ) );
	strcpy( script->filename, name );
	script->buffer = (char *) buffer + sizeof( script_t );
	script->buffer[length] = 0;
	script->length = length;
	//pointer in script buffer
	script->script_p = script->buffer;
	//pointer in script buffer before reading token
	script->lastscript_p = script->buffer;
	//pointer to end of script buffer
	script->end_p = &script->buffer[length];
	//set if there's a token available in script->token
	script->tokenavailable = 0;
	//
	script->line = 1;
	script->lastline = 1;
	//
	SetScriptPunctuations( script, NULL );
	//
	memcpy( script->buffer, ptr, length );
	//
	return script;
} //end of the function LoadScriptMemory
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void FreeScript( script_t *script ) {
#ifdef PUNCTABLE
	if ( script->punctuationtable ) {
		FreeMemory( script->punctuationtable );
	}
#endif //PUNCTABLE
	FreeMemory( script );
} //end of the function FreeScript

/*
============
Q_vsnprintf - copied from reference implementation in common.c

vsnprintf portability:

C99 standard: vsnprintf returns the number of characters (excluding the trailing
'\0') which would have been written to the final string if enough space had been available
snprintf and vsnprintf do not write more than size bytes (including the trailing '\0')

win32: _vsnprintf returns the number of characters written, not including the terminating null character,
or a negative value if an output error occurs. If the number of characters to write exceeds count,
then count characters are written and -1 is returned and no trailing '\0' is added.

Q_vsnPrintf: always append a trailing '\0', returns number of characters written or
returns -1 on failure or if the buffer would be overflowed.
============
*/
int Q_vsnprintf( char *dest, int size, const char *fmt, va_list argptr ) {
	int ret;

#ifdef _WIN32
#undef _vsnprintf
	ret = _vsnprintf( dest, size - 1, fmt, argptr );
#define _vsnprintf  use_idStr_vsnPrintf
#else
#undef vsnprintf
	ret = vsnprintf( dest, size, fmt, argptr );
#define vsnprintf   use_idStr_vsnPrintf
#endif
	dest[size - 1] = '\0';
	if ( ret < 0 || ret >= size ) {
		return -1;
	}
	return ret;
}
